#define RTSP_C

/*----------------------------------------------*
 * include files            	 ������ͷ�ļ�   *
 *----------------------------------------------*/
#if defined(OS_ANDROID)||defined(OS_LINUX_SOC)
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <err.h>
#elif defined(OS_WIN32)
#include <winsock2.h>
#include <ws2tcpip.h>
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>

//user header
#include <libmscommon/mscommon.h>
#include <libmscommon/mstime.h>
#include <libmscommon/msstring.h>
#include <libmscommon/msnetwork.h>
#include <libmscommon/msenv.h>
#include <libmslog/mslog.h>
#include "msprotocol.h"

 #define FLAG "RTSP"
 
extern URLProtocol ffurl_tcp_protocol;
extern URLProtocol ffurl_udp_protocol;
extern URLProtocol ffurl_rtp_protocol;
static unsigned int rtp_port=mscfg_rtspclient_baseport;
//不发送结束符的RTSP会话测试
#define RTSP_DBG_NO_ENDLINE 					ms_false
//RTSP(over TCP)发送心跳测试
#define RTSP_DBG_OVERTCP_SENDHEART 				ms_false
//RTSP会话未完成时，主动关闭会话测试
#define RTSP_DBG_CLOSE_SessionIncomplete 		ms_false
//RTSP录制会话没有会话ID
#define RTSP_DBG_RECORD_NOID 					ms_false


#define RTSP_REPLAY_OK	" 200"
#define SPACE			" "
#define RTSP_LINEEND_FLAG 	"\r\n"
#define RTSP_SDPEND_FLAG 	"\r\n\r\n"
#define RTSP_SEM_FLAG 		";"
#define	FILER_PUBLIC			"Public:"
#define	FILER_LOCATION			"Location: "
#define	FILER_TRACKID			"control:"	
#define	FILER_SESSION	 		"Session:"
#define	FILER_TRANSPORT	 	"Transport:"
#define	FILER_PTTYPE_VIDEO		"m=video"	//m=video 0 MP2T/AVP 33,m=video 0 RTP/AVP 96,m=video 0 RTP/AVP 33,a=rtpmap:33 MP2T/90000
#define	FILER_RTPMAP		           "a=rtpmap:"
#define	FILER_PTTYPE_AUDIO		"m=audio"	//m=audio 0 RTP/AVP 8

static char *pcmd_string[20]={
		"UNVAIL",	//unvail
		"OPTIONS",
		"DESCRIBE",
		"ANNOUNCE",
		"SETUP",
		"PLAY",
		"RECORD",
		"PAUSE",
		"GET_PARAMETER" ,
		"SET_PARAMETER" ,		
		"TEARDOWN"     
	};	
typedef enum RTSPCMD_TYPE{
	RTSPCMD_UNKNOW			=0x00, 
	RTSPCMD_OPTIONS     	,
	RTSPCMD_DESCRIBE    	,
	RTSPCMD_ANNOUNCE    ,
	RTSPCMD_SETUP     	,
	RTSPCMD_PLAY    		,
	RTSPCMD_RECORD    	,
	RTSPCMD_PAUSE    	,	
	RTSPCMD_GET_PARAMETER    ,
	RTSPCMD_SET_PARAMETER    ,	
	RTSPCMD_TEARDOWN
}ENUM_RTSPCMD_TYPE;
typedef enum RTSPClientState {
    RTSP_STATE_IDLE,    /**< not initialized */
    RTSP_STATE_STREAMING, /**< initialized and sending/receiving data */
    RTSP_STATE_PAUSED,  /**< initialized, but not receiving data */
    RTSP_STATE_SEEKING, /**< initialized, requesting a seek */
}RTSPClientState;
typedef enum RTSP_STATUS{
	RTSP_UNKNOW=0x00,
	RTSP_REPLY_OK,
	RTSP_NONEED_REPLY,
	RTSP_SENDCMD_FAILED	,
	RTSP_REPLY_FAILED	,
	RTSP_REPLY_NOSDP	,
	RTSP_REPLY_READ_AGAIN	,
	RTSP_REPLY_REOPEN	,
	RTSP_REPLY_TRANSPORT	,
	RTSP_REPLY_NOT_IMPLEMENTED,
	
	RTSP_CONNECT_OK	,
	RTSP_CONNECT_FAILED,

	RTSP_FUNC_OK		,
	RTSP_FUNC_FAILED	,
	RTSP_PARAM_ERR		,

	RTSP_ERR_SOCKET_FD ,
	RTSP_HAS_CLOSE_SOCKET_FD ,
	RTSP_SHOULD_CLOSE_SOCKET_FD ,

	RTSP_READ_AGAIN,
	RTSP_READ_EOF,
}ENUM_RTSP_STATUS;
typedef enum COPY_TYPE
{
	COPY_CTT_ONLY				=0x00,		//copy content
	COPY_CTT_FILTER				,			//copy filter  + content
	COPY_CTT_TAIL				,			//copy content + tail(string_tail1 or string_tail2)
	COPY_CTT_FILTER_TAIL			,		//copy filter  + content + tail(string_tail1 or string_tail2)
	COPY_CTT_FILTER_PLUS_LINEEND	,		//copy filter  + content + lineend("\r\n")
}ENUM_COPY_TYPE;

typedef enum RTSPServerType {
    RTSP_SERVER_RTP,  /**< Standards-compliant RTP-server */
    RTSP_SERVER_REAL, /**< Realmedia-style server */
    RTSP_SERVER_WMS,  /**< Windows Media server */
    RTSP_SERVER_NB
}RTSPServerType;
typedef enum RTSPTransport {
    RTSP_STATUS_TRANSPORT_RTP, /**< Standards-compliant RTP */
    RTSP_STATUS_TRANSPORT_RDT, /**< Realmedia Data Transport */
    RTSP_STATUS_TRANSPORT_RAW, /**< Raw data (over UDP) */
    RTSP_STATUS_TRANSPORT_NB
}RTSPTransport;

typedef struct PUBLICInfo{
	//Public: DESCRIBE, SETUP, PLAY, PAUSE, TEARDOWN, SET_PARAMETER, GET_PARAMETER, OPTIONS
	ms_bool has_options;
	ms_bool has_describe;
	ms_bool has_setup;	
	ms_bool has_play;	
	ms_bool has_pause;	
	ms_bool has_teardown;	
	ms_bool has_setparameter;	
	ms_bool has_getparameter;	
	ms_bool has_announce;	
	ms_bool has_redirect;	
	ms_bool has_record;	
}PUBLICInfo;

typedef struct RtspContent{
	RTSPClientState rtsp_state;
	ms_s08 session[80];    
	ms_u08 num_track;
	ms_s08 control_trackid[5][256];	//maybe it is a url/tackID or tackID
	ms_s08 useragent[256];
	RTPPAYLOADType payload_type_video;
	RTPPAYLOADType payload_type_audio;
	ms_bool flag_hassdp;
	ms_u32  cseq_id;	
	time_t timep;
	ms_s08 transport[256]; 
	ms_s08 publics[256]; 
	ms_s08 location[mscfg_maxlen_url];	//Redirecting to new url
	PUBLICInfo public_info;
	RTSPServerType server_type;		//res
	ms_s64 seek_timestamp;

	ms_bool  has_tryother_transport;
	URLContext url_rtspctt;	
	/* BEGIN: Added by sugao<sugao_cn@163.com>, 2019/11/11   PN:001,Server   ANNOUNCE used */
	WEBREQUESTContext request_ctt;
	/* END:   Added by sugao, 2019/11/11 */
//param
	ms_byte client_host[64];
	ms_bool flag_test_noin;
	ms_byte rtsp_transport[64];

	ms_byte rtsp_info[256];
}RTSPContent;

static ms_bool rtsp_parse_sdp(char * filter, char *bufin,int sdpctt_index, 
					ENUM_COPY_TYPE copy_type, char * bufout, char * string_tail1, char * string_tail2)
{
	int context_len=0;
	char * ptail=ms_null;
	
	if (ms_null==filter|| ms_null==bufin || ms_null==bufout){
		ms_errRet(ms_false,"Param error" );
	}
		
	if ( strlen(bufout) 
		&&ms_strncmp_sneq(filter,FILER_TRACKID)
		&&ms_strncmp_sneq(filter,FILER_LOCATION)	
	){
		return ms_false;
	}
	ms_bool flag_find=ms_false;
	if ( ms_strncmp_seq(&bufin[sdpctt_index], filter)	){
		ms_verbose("YES,I find  it Get info:[filter->%s] %s", filter, &bufin[sdpctt_index] );
		for ( context_len=0;  context_len < strlen(&bufin[sdpctt_index]) ;  context_len++	){
			if (  ms_null!=string_tail1 && ms_strncmp_seq(&bufin[sdpctt_index+strlen(filter)+context_len], string_tail1  )  	){
				ptail = string_tail1;
				break;
			}else if ( ms_null!=string_tail2 && ms_strncmp_seq(&bufin[sdpctt_index+strlen(filter)+context_len], string_tail2  ) 	){
				ptail = string_tail2;
				break;
			}
		}
		//ms_info("Len:%d 	%ld", context_len, strlen(&bufin[sdpctt_index])	);
		switch(copy_type){
			case	COPY_CTT_ONLY:
				memcpy( bufout, &bufin[sdpctt_index+strlen(filter)],  context_len	 );
				bufout[context_len]=0;
				break;
			case	COPY_CTT_FILTER:
				memcpy( bufout, &bufin[sdpctt_index], strlen(filter)+ context_len	 );
				bufout[strlen(filter)+ context_len]=0;
				break;
			case	COPY_CTT_TAIL:
				memcpy( bufout, &bufin[sdpctt_index+strlen(filter)], context_len+strlen(ptail)	);
				bufout[context_len+strlen(ptail)]=0;
				break;
			case	COPY_CTT_FILTER_TAIL:
				memcpy( bufout, &bufin[sdpctt_index], strlen(filter)+context_len+strlen(ptail)	);
				bufout[strlen(filter)+context_len+strlen(ptail)]=0;
				break;
			case	COPY_CTT_FILTER_PLUS_LINEEND:
				memcpy( bufout, &bufin[sdpctt_index], strlen(filter)+ context_len );
				sprintf( &( bufout[strlen(bufout) ] ), "%s", RTSP_LINEEND_FLAG );
				bufout[strlen(filter)+ context_len+strlen(RTSP_LINEEND_FLAG)]=0;
				break;
		}	

		flag_find=ms_true;
	}
	return flag_find;
}

static void rtsp_parse_publics(RTSPContent *rtsp_ctt)
{
	//Public: DESCRIBE, SETUP, PLAY, PAUSE, TEARDOWN, SET_PARAMETER, GET_PARAMETER, OPTIONS
	if(0==ms_strlen(rtsp_ctt->publics)){
		return ;
	}
	ms_memset(&rtsp_ctt->public_info, 0, sizeof(PUBLICInfo));
	char buf[256]; 
	ms_strcpy(buf, rtsp_ctt->publics)
	ms_string str_array_item[256];
	int num1=msstr_api_split(buf, ",", str_array_item);
	int index=0;
	//ms_debug("buf:%s",buf);
	for(index=0;index<num1;index++){
		//ms_verbose("%02d.%s",index,str_array_item[index]);
		if(ms_false!=msstr_api_isCasestr(str_array_item[index], "OPTIONS")){
			rtsp_ctt->public_info.has_options=ms_true;
		}else if(ms_false!=msstr_api_isCasestr(str_array_item[index], "DESCRIBE")){
			rtsp_ctt->public_info.has_describe=ms_true;
		}else if(ms_false!=msstr_api_isCasestr(str_array_item[index], "SETUP")){
			rtsp_ctt->public_info.has_setup=ms_true;
		}else if(ms_false!=msstr_api_isCasestr(str_array_item[index], "PLAY")){
			rtsp_ctt->public_info.has_play=ms_true;
		}else if(ms_false!=msstr_api_isCasestr(str_array_item[index], "PAUSE")){
			rtsp_ctt->public_info.has_pause=ms_true;
		}else if(ms_false!=msstr_api_isCasestr(str_array_item[index], "TEARDOWN")){
			rtsp_ctt->public_info.has_teardown=ms_true;
		}else if(ms_false!=msstr_api_isCasestr(str_array_item[index], "SET_PARAMETER")){
			rtsp_ctt->public_info.has_setparameter=ms_true;
		}else if(ms_false!=msstr_api_isCasestr(str_array_item[index], "GET_PARAMETER")){
			rtsp_ctt->public_info.has_getparameter=ms_true;
		}else if(ms_false!=msstr_api_isCasestr(str_array_item[index], "ANNOUNCE")){
			rtsp_ctt->public_info.has_announce=ms_true;
		}else if(ms_false!=msstr_api_isCasestr(str_array_item[index], "REDIRECT")){
			rtsp_ctt->public_info.has_redirect=ms_true;
		}else if(ms_false!=msstr_api_isCasestr(str_array_item[index], "RECORD")){
			rtsp_ctt->public_info.has_record=ms_true;
		}
	}
}
static ms_bool rtsp_parse_transport(URLContext *s,RTSPContent *rtsp_ctt)
{
//source=27.148.240.197;server_port=32166-32167;ssrc=776437
#define FILER_SOURCE		"source"
#define FILER_SERVERPORT	"server_port"
#define FILER_SSRC			"ssrc"
	if(0==ms_strlen(rtsp_ctt->transport)){
		return ms_false;
	}
	char buf[256]; 
	ms_strcpy(buf, rtsp_ctt->transport)
	ms_string str_array_item[256];
	int num1=msstr_api_split(buf, ";", str_array_item);
	int index=0;
	ms_verbose("buf:%s",buf);
	for(index=0;index<num1;index++){
		ms_verbose("%02d.%s",index,str_array_item[index]);
		if(ms_strncmp_seq(str_array_item[index], FILER_SOURCE)){
			ms_strcpy(s->opt.rtsp.transport_info.source,&str_array_item[index][ms_strlen(FILER_SOURCE)+1]);
			ms_verbose("source=%s",s->opt.rtsp.transport_info.source);
		}else if(ms_strncmp_seq(str_array_item[index], FILER_SERVERPORT)){
			char server_port[256]={0};
			ms_strcpy(server_port,str_array_item[index]);
			int index_1=0;
			for(index_1=0;index_1<ms_strlen(server_port);index_1++){
				if(ms_strncmp_seq(&str_array_item[index][ms_strlen(FILER_SERVERPORT)+1+index_1], "-")){
					break;
				}
			}
			s->opt.rtsp.transport_info.serverport_rtp=
				atoi(&str_array_item[index][ms_strlen(FILER_SERVERPORT)+1]);
			s->opt.rtsp.transport_info.serverport_rtcp=
				atoi(&str_array_item[index][ms_strlen(FILER_SERVERPORT)+1+index_1+1]);
			ms_verbose("serverport_rtp=%d",s->opt.rtsp.transport_info.serverport_rtp);
			ms_verbose("serverport_rtcp=%d",s->opt.rtsp.transport_info.serverport_rtcp);
		}else if(ms_strncmp_seq(str_array_item[index], FILER_SSRC)){
			s->opt.rtsp.transport_info.ssrc=atoi(&str_array_item[index][ms_strlen(FILER_SSRC)+1]);
			ms_verbose("ssrc=%d",s->opt.rtsp.transport_info.ssrc);
		}
		if(0==ms_buflen(s->opt.rtsp.transport_info.source)){
			ms_strcpy(s->opt.rtsp.transport_info.source,s->url_info.hostname);
			ms_verbose("set source to hostname: %s",s->opt.rtsp.transport_info.source);
		}
	}

	if((RTSP_LOWER_TRANSPORT_UDP==s->opt.rtsp.lower_transport)
		&&(!(s->opt.rtsp.transport_info.serverport_rtp>0)
			||!(s->opt.rtsp.transport_info.serverport_rtcp>0))
	){
		//return ms_false;
		ms_waring("no %s",FILER_SERVERPORT);
	}
	return ms_true;
}
static ENUM_RTSP_STATUS rtsp_parse_reply(RTSPContent *rtsp_ctt,char *buf, int sitLen)
{							
	int rtsp_statuscode=0;
	ENUM_RTSP_STATUS rtsp_status=RTSP_REPLY_FAILED;

	char *bufin=buf;
//RTSP/1.0 200
	if ( ms_null==bufin || ms_null==rtsp_ctt ){
		ms_error("Param error");
		return RTSP_PARAM_ERR;
	}
	if (  ms_strncmp_seq(bufin, MSPTC_VERSION_RTSP_10 )	||ms_strncmp_seq(bufin, MSPTC_VERSION_RTSP_11 )){
		rtsp_statuscode=ms_atoi(&bufin[strlen(MSPTC_VERSION_RTSP)]	);
		msptc_api_printStatusCode(rtsp_statuscode);
		if (ms_true!=msptc_api_statusCode_isOK(rtsp_statuscode) ){
			if (ms_true==msptc_api_statusCode_isLocation(rtsp_statuscode) ){
				rtsp_status=RTSP_REPLY_REOPEN;
			}else if(rtsp_statuscode ==NETSTATE_CODE_TRANSPORT ) {
				rtsp_status=RTSP_REPLY_TRANSPORT;
				goto end;
			}else if(rtsp_statuscode ==NETSTATE_CODE_NOT_IMPLEMENTED ) {
				rtsp_status=RTSP_REPLY_NOT_IMPLEMENTED;
				goto end;
			}else{	//(reply->status_code > 400)
				ms_error( "RtspStatusCode(%d) Error, %s", rtsp_statuscode, bufin );
				rtsp_status=RTSP_REPLY_FAILED;
				goto end;
			}
		}else{
			rtsp_status=RTSP_REPLY_OK;
		}
	}
	ms_s32 sdpctt_index=0;	
	for(sdpctt_index=0; sdpctt_index<sitLen; sdpctt_index++){
		rtsp_parse_sdp(FILER_LOCATION	, bufin,  sdpctt_index, 
			COPY_CTT_ONLY, rtsp_ctt->location, RTSP_LINEEND_FLAG, ms_null);
		if(ms_true==rtsp_parse_sdp(FILER_TRACKID	, bufin,  sdpctt_index, 
			COPY_CTT_ONLY, rtsp_ctt->control_trackid[ rtsp_ctt->num_track], RTSP_LINEEND_FLAG, ms_null)){
			if(ms_buflen((rtsp_ctt->control_trackid[ rtsp_ctt->num_track] ))>0
				&&ms_strncmp_sneq("*",(rtsp_ctt->control_trackid[ rtsp_ctt->num_track] ) )	){
				rtsp_ctt->num_track+=1;
			}
		}

		ms_u08 temp[128]={0};
		rtsp_parse_sdp(FILER_PTTYPE_VIDEO	, bufin,  sdpctt_index, 
			COPY_CTT_ONLY, temp, RTSP_LINEEND_FLAG, ms_null);
		if(ms_buflen(temp)>0){
			rtsp_ctt->flag_hassdp=ms_true;
			if((msstr_api_isCasestr(temp, "MP2T/AVP 33") !=ms_false)||(msstr_api_isCasestr(temp, "RTP/AVP 33") !=ms_false)){
				rtsp_ctt->payload_type_video=RTP_PAYLOADTYPE_DATA_MP2T;
			}else if(msstr_api_isCasestr(temp, "RTP/AVP 96") !=ms_false
					||msstr_api_isCasestr(temp, "RTP/AVP 97") !=ms_false
					||msstr_api_isCasestr(temp, "RTP/AVP 98") !=ms_false){
				rtsp_ctt->payload_type_video=RTP_PAYLOADTYPE_CUSTOM;
			}else{
				rtsp_ctt->payload_type_video=RTP_PAYLOADTYPE_UNKNOW;
			}
		}
		ms_buf0(temp);
		rtsp_parse_sdp(FILER_PTTYPE_AUDIO	, bufin,  sdpctt_index, 
			COPY_CTT_ONLY, temp, RTSP_LINEEND_FLAG, ms_null);
		if(ms_buflen(temp)>0){
			rtsp_ctt->flag_hassdp=ms_true;
			if(msstr_api_isCasestr(temp, "RTP/AVP 8") !=ms_false){
				rtsp_ctt->payload_type_audio=RTP_PAYLOADTYPE_AUDIO_PCMA;
			}if(msstr_api_isCasestr(temp, "RTP/AVP 14") !=ms_false){
				rtsp_ctt->payload_type_audio=RTP_PAYLOADTYPE_AUDIO_PCMA;
			}else{
				rtsp_ctt->payload_type_audio=RTP_PAYLOADTYPE_UNKNOW;
			}
		}
		rtsp_parse_sdp(FILER_SESSION	, bufin,  sdpctt_index, 
			COPY_CTT_FILTER_PLUS_LINEEND, rtsp_ctt->session, RTSP_LINEEND_FLAG, RTSP_SEM_FLAG);
		rtsp_parse_sdp(FILER_TRANSPORT	, bufin,  sdpctt_index, 
			COPY_CTT_ONLY, rtsp_ctt->transport, RTSP_LINEEND_FLAG, ms_null);
		rtsp_parse_sdp(FILER_PUBLIC	, bufin,  sdpctt_index, 
			COPY_CTT_ONLY, rtsp_ctt->publics, RTSP_LINEEND_FLAG, ms_null);		
	}
end:	
	return rtsp_status;
}

/* BEGIN: Added by sugao<sugao_cn@163.com>, 2019/11/11   PN:001,Server   ANNOUNCE used */
static ms_void rtsp_parse_announec(URLContext *s ,char *buf)
{
	RTSPContent *rtsp_ctt=s->priv_data;
	msptc_api_parseRequest(buf, &rtsp_ctt->request_ctt,ms_null);
	if(ms_true==msstr_api_isCasestr(rtsp_ctt->request_ctt.protocol, "RTSP") ){
		char tempbuf[1024]={0};
		ms_s08 strCurDTime[64]={0};
		ms_sprintfs(tempbuf,
			"%s 200 OK\r\n"
			"CSeq: %s\r\n"
			"Date: %s GMT\r\n"
			"Session: %s\r\n"
			"\r\n",
			rtsp_ctt->request_ctt.protocol,
			rtsp_ctt->request_ctt.extern_data.rtsp_rd.CSeq,
			mstime_api_curDTime(mstime_fmt_datetime,strCurDTime),
			rtsp_ctt->request_ctt.extern_data.rtsp_rd.Session
		);	
		if((ms_true==msstr_api_isCasestr(rtsp_ctt->request_ctt.method, "ANNOUNCE"))
			&&ms_buflen(rtsp_ctt->request_ctt.url)>0){
			//update url
			ms_strcpy(s->opt.rtsp.announce_url, rtsp_ctt->request_ctt.url);
		}
		URLContext *purl_rtspctt=&rtsp_ctt->url_rtspctt;
		if(msnet_api_epollSend3(purl_rtspctt->fd , tempbuf, ms_buflen(tempbuf))<0){
			ms_error("msnet_api_epollSend3 failed");
		}
	}
}
/* END:   Added by sugao<sugao_cn@163.com>, 2019/11/11 */

 static ENUM_RTSP_STATUS rtsp_send_cmd(ENUM_RTSPCMD_TYPE enumRtspCmd,URLContext *s,ms_bool is_keeplive_cmd)
{
	//ALL:"OPTIONS","DESCRIBE","SETUP","PLAY","PAUSE","GET_PARAMETER" ,"TEARDOWN"  
	char * http_cmd=ms_null;
	char * url=ms_null;	
	char	ascHTTPVersion[20]={0};
	char	aucCSeqId[20]		={0};
	char * pscUserAgent=ms_null;	

	//DESCRIBE
	char * pscAccept="pscAccept: application/sdp\r\n";	
	//SETUP	
	char * pscTrackID=ms_null;
	char ascTransport[128]={0};		
	//PLAY,TEARDOWN
	char * pscSession=ms_null;
	char ascRangeBuf[40]={0};

	RTSPContent *rtsp_ctt=s->priv_data;
	URLProtocol *tcp_func=s->opt.rtsp.tcp_func;
	URLContext *purl_rtspctt=&rtsp_ctt->url_rtspctt;
	
	pscUserAgent=rtsp_ctt->useragent;
	
	//<1>get cmd string:	"OPTIONS","DESCRIBE","SETUP","PLAY","PAUSE","GET_PARAMETER" ,"TEARDOWN"   
	http_cmd=pcmd_string[enumRtspCmd];
	
	//<2>get rtsp url
	/* BEGIN: Added by sugao<sugao_cn@163.com>, 2019/11/11   PN:001,Server   ANNOUNCE used */
	url=  (ms_buflen(s->opt.rtsp.announce_url)>0) ? s->opt.rtsp.announce_url  :  \
		((ms_true==s->flag_location) ? s->location  : s->url );
	/* END:   Added by sugao<sugao_cn@163.com>, 2019/11/11 */
	
	//<3>get httpversion
	sprintf(ascHTTPVersion," %s%s",MSPTC_VERSION_RTSP, RTSP_LINEEND_FLAG);
	//<4>get cseq id
	rtsp_ctt->cseq_id+= 1 ;
	sprintf(aucCSeqId,"CSeq: %d%s",rtsp_ctt->cseq_id, RTSP_LINEEND_FLAG);
	//<4>get sdpfile
	char buf_sendcmd[1600]	={0};	
	char buf_sendcmd_tmp[1600]	={0};	
	pscSession=rtsp_ctt->session;
	char *trans_pref;
	switch ( enumRtspCmd ){
	    case RTSPCMD_OPTIONS :
			sprintf(buf_sendcmd,"%s %s%s%s%s",http_cmd,url,ascHTTPVersion,pscUserAgent,aucCSeqId);
			if(ms_strlen(pscSession)){
				ms_strcat(buf_sendcmd,pscSession);
			}
	        break;
	    case RTSPCMD_DESCRIBE:	
	  		sprintf(buf_sendcmd,"%s %s%s%s%s%s",http_cmd,url,ascHTTPVersion,pscAccept,pscUserAgent,aucCSeqId);	
			if(ms_strlen(pscSession)){
				ms_strcat(buf_sendcmd,pscSession);
			}
	        break;
	    case RTSPCMD_ANNOUNCE:	
	  		sprintf(buf_sendcmd,"%s %s%s%s%s",http_cmd,url,ascHTTPVersion,pscUserAgent,aucCSeqId);	
			if(ms_strlen(pscSession)){
				ms_strcat(buf_sendcmd,pscSession);
			}
			int content_len=ms_strlen(msinfo_sdp);	
			ms_strcats(buf_sendcmd_tmp,buf_sendcmd,"Content-Type: %s\r\n","application/sdp");
			ms_strcats(buf_sendcmd_tmp,buf_sendcmd,"Content-Length: %d\r\n",content_len);
	        break;
	    case RTSPCMD_SETUP:
			if (s->opt.rtsp.transport == RTSP_STATUS_TRANSPORT_RDT){
        				trans_pref = "x-pn-tng";
			}else if (s->opt.rtsp.transport == RTSP_STATUS_TRANSPORT_RAW){
				trans_pref = "RAW/RAW";
			}else{
				trans_pref = "RTP/AVP";
			}
			ms_u08 index_track=0;
			ms_verbose("There is %d tracks,default to use the first trackid ", rtsp_ctt->num_track);
			pscTrackID=rtsp_ctt->control_trackid[0];
			for(index_track=0;index_track<rtsp_ctt->num_track;index_track++){
				ms_verbose("[track%3d]:%s", (index_track+1),rtsp_ctt->control_trackid[index_track]);
				if((ms_true==msstr_api_isCasestr(rtsp_ctt->control_trackid[index_track], "trackID"))
					||(ms_true==msstr_api_isCasestr(rtsp_ctt->control_trackid[index_track], "trackid"))){
					pscTrackID=rtsp_ctt->control_trackid[index_track];
					ms_verbose("uses [track%3d]", (index_track+1));
					break;
				}
			}
			
			if(RTSP_LOWER_TRANSPORT_TCP==s->opt.rtsp.lower_transport){
				if(s->flags&FLAG_WRITE){
					sprintf(ascTransport, "Transport: %s/TCP;unicast;interleaved=%d-%d;mode=record\r\n",
						trans_pref,RTSP_DATATYPE_RTP,RTSP_DATATYPE_RTCP);
					ms_sprintfs(pscTrackID, "%s", "trackid1");
				}else{
					sprintf(ascTransport, "Transport: %s/TCP;unicast;interleaved=%d-%d\r\n",
						trans_pref,RTSP_DATATYPE_RTP,RTSP_DATATYPE_RTCP);	
				}
			}else{
				s->opt.rtsp.rtp_port=rtp_port;
				rtp_port+=2;
				if((rtp_port+2)>mscfg_rtspclient_baseport_max){
					rtp_port=mscfg_rtspclient_baseport;
				}
				if(s->flags&FLAG_WRITE){
					sprintf(ascTransport,"Transport: %s/UDP;unicast;client_port=%d-%d;mode=record\r\n",trans_pref,
						s->opt.rtsp.rtp_port,s->opt.rtsp.rtp_port+1);
					ms_sprintfs(pscTrackID, "%s", "trackid1");

				}else{
					if(ms_buflen(rtsp_ctt->client_host)>0){
						sprintf(ascTransport,"Transport: %s/UDP;unicast;client_port=%d-%d;client_host=%s\r\n",trans_pref,
							s->opt.rtsp.rtp_port,s->opt.rtsp.rtp_port+1,rtsp_ctt->client_host);
					}else{
						sprintf(ascTransport,"Transport: %s/UDP;unicast;client_port=%d-%d\r\n",trans_pref,
							s->opt.rtsp.rtp_port,s->opt.rtsp.rtp_port+1);
					}
				}
					
			}	
			if(ms_true==msptc_api_isUrl(pscTrackID)){
				sprintf(buf_sendcmd,"%s %s%s%s%s%s",http_cmd,pscTrackID,ascHTTPVersion,ascTransport,pscUserAgent,aucCSeqId);	
			}else{
				sprintf(buf_sendcmd,"%s %s/%s%s%s%s%s",http_cmd,url,pscTrackID,ascHTTPVersion,ascTransport,pscUserAgent,aucCSeqId);	
			}
			if(ms_strlen(pscSession)){
				ms_strcat(buf_sendcmd,pscSession);
			}
			break;
		case RTSPCMD_PAUSE:
			sprintf(buf_sendcmd,"%s %s%s%s%s%s",http_cmd,url,ascHTTPVersion,pscSession,pscUserAgent,aucCSeqId);	
			rtsp_ctt->rtsp_state=RTSP_STATE_PAUSED;
	    case RTSPCMD_PLAY:		
			if(rtsp_ctt->rtsp_state=RTSP_STATE_PAUSED){
				ascRangeBuf[0]=0;
			}else{
			    sprintf(ascRangeBuf,"Range: npt=%s.%s-\r\n",
					ms_num2str(rtsp_ctt->seek_timestamp / 1000000),
					ms_num2str(rtsp_ctt->seek_timestamp / (1000000 / 1000) % 1000));
			}
			sprintf(buf_sendcmd,"%s %s%s%s%s%s%s",http_cmd,url,ascHTTPVersion,ascRangeBuf,pscSession,pscUserAgent,aucCSeqId);	
			rtsp_ctt->rtsp_state=RTSP_STATE_STREAMING;
			break;	
	    case RTSPCMD_RECORD:		
		#if RTSP_DBG_RECORD_NOID
			sprintf(buf_sendcmd,"%s %s%s%s%s%s",http_cmd,url,ascHTTPVersion,"Session: \r\n",pscUserAgent,aucCSeqId);
		#else
			sprintf(buf_sendcmd,"%s %s%s%s%s%s",http_cmd,url,ascHTTPVersion,pscSession,pscUserAgent,aucCSeqId);	
		#endif
			rtsp_ctt->rtsp_state=RTSP_STATE_STREAMING;
			break;	
	    case RTSPCMD_GET_PARAMETER:			
			sprintf(buf_sendcmd,"%s %s/%s%s%s%s",http_cmd,url,ascHTTPVersion,pscUserAgent,pscSession,aucCSeqId);	
	        break;	
	    case RTSPCMD_SET_PARAMETER:		
			ms_waring("SET_PARAMETER isnot support,please fix it");
			sprintf(buf_sendcmd,"%s %s/%s%s%s%s",http_cmd,url,ascHTTPVersion,pscUserAgent,pscSession,aucCSeqId);	
	        break;				
	    case RTSPCMD_TEARDOWN:			
			sprintf(buf_sendcmd,"%s %s%s%s%s%s",http_cmd,url,ascHTTPVersion,pscSession,pscUserAgent,aucCSeqId);
	        break;
	    default:
			ms_info("Error ( enumRtspCmd )");
		break;
	}
#if RTSP_DBG_NO_ENDLINE
        ms_waring("This is an RTSP session test that does not send terminators. Please turn off this function when it is officially used.");
#else
	sprintf( &(buf_sendcmd[strlen(buf_sendcmd)]), "%s", RTSP_LINEEND_FLAG  );
#endif
	if(RTSPCMD_ANNOUNCE==enumRtspCmd){
		ms_strcat(buf_sendcmd,msinfo_sdp);
	}
	ms_verbose( "[%s-sendCmd]:\r\n%s",http_cmd,buf_sendcmd);	
	int len=tcp_func->url_write(purl_rtspctt,(unsigned char *)buf_sendcmd,strlen(buf_sendcmd));
	if ( len!=strlen(buf_sendcmd) ){
		ms_error("Send cmd failed(%d %d)",(int)strlen(buf_sendcmd),len);
		return RTSP_SENDCMD_FAILED;
	}
//	if(RTSPCMD_OPTIONS==enumRtspCmd && rtsp_ctt->cseq_id>4){
//		return RTSP_REPLY_OK;
//	}
	//TCP maybe read live-data,I read the reply in rtp read and then drop it;
	if(RTSP_LOWER_TRANSPORT_TCP==s->opt.rtsp.lower_transport
		&& (ms_true==is_keeplive_cmd)
		&&(RTSPCMD_GET_PARAMETER==enumRtspCmd||RTSPCMD_OPTIONS==enumRtspCmd)		){
		return RTSP_REPLY_OK;
	}
	
	if((RTSPCMD_TEARDOWN==enumRtspCmd)
		&&(RTSP_LOWER_TRANSPORT_TCP==s->opt.rtsp.lower_transport)){
		return RTSP_REPLY_OK;
	}
	
	ENUM_RTSP_STATUS rtsp_status=RTSP_UNKNOW;
	char aucReplyContext[38572]={0};
	char reread_time=0;
ms_reread:	
	len=tcp_func->url_read(purl_rtspctt,( uint8_t *)aucReplyContext, sizeof(aucReplyContext) );
	if ( len>0 ){
		ms_verbose("[%s-ReadReply-%d]:\r\n%s",http_cmd,len,aucReplyContext);
		rtsp_status=rtsp_parse_reply(rtsp_ctt,aucReplyContext, len) ;
		if(RTSPCMD_OPTIONS==enumRtspCmd
			&&RTSP_REPLY_NOT_IMPLEMENTED!=rtsp_status){
			rtsp_parse_publics(rtsp_ctt);
		}
		if(RTSPCMD_DESCRIBE==enumRtspCmd ){
			if(RTSP_REPLY_OK==rtsp_status){
				if(ms_false==rtsp_ctt->flag_hassdp){
					ms_waring("No sdp file,reread");
					len=tcp_func->url_read(purl_rtspctt,( uint8_t *)aucReplyContext, sizeof(aucReplyContext) );
					if ( len>0 ){
						rtsp_parse_reply(rtsp_ctt,aucReplyContext, len) ;
						if(ms_false==rtsp_ctt->flag_hassdp){
							rtsp_status=RTSP_REPLY_NOSDP;
						}
					}else{
						goto ms_failed;
					}
				}
			}
		}
		if(RTSPCMD_SETUP==enumRtspCmd ){
			if((rtsp_status!=RTSP_REPLY_TRANSPORT)
				&&ms_false==rtsp_parse_transport(s,rtsp_ctt)){
				ms_error("rtsp_parse_transport failed");
				rtsp_status=RTSP_REPLY_FAILED;
			}
		}
	}else{
ms_failed:
		ms_error("[%s-ReadReply-(len=%d)]:reread_time=%d",http_cmd,len,reread_time);
		if((reread_time++)<3){
			goto ms_reread;
		}else{
			rtsp_status=RTSP_REPLY_FAILED;
		}
	}
	return rtsp_status;
}

static int rtsp_play(URLContext *s)
{
	if ( RTSP_REPLY_OK!=rtsp_send_cmd(RTSPCMD_PLAY, s,ms_false)	){
		ms_errRet(-1,"send PALY failed!");
	}
	return 0;
}
static int rtsp_pause(URLContext *s)
{
	if ( RTSP_REPLY_OK!=rtsp_send_cmd(RTSPCMD_PAUSE, s,ms_false)	){
		ms_errRet(-1,"send PAUSE failed!");
	}
	return 0;	
}
static int rtsp_seek(URLContext *s,int64_t timestamp)
{
	RTSPContent *rtsp_ctt=s->priv_data;
	
    rtsp_ctt->seek_timestamp = timestamp*1000000/90000000;
    switch(rtsp_ctt->rtsp_state) {
		default:
    	case RTSP_STATE_IDLE:
        	break;
    	case RTSP_STATE_STREAMING:
        if ((rtsp_pause(s)) != 0){
			return -1;
		} 
        rtsp_ctt->rtsp_state = RTSP_STATE_SEEKING;
        if ((rtsp_play(s)) != 0){
			return -1;
		}  
        break;
    case RTSP_STATE_PAUSED:
        rtsp_ctt->rtsp_state = RTSP_STATE_IDLE;
        break;
    }
    return 0;
}

static void rtsp_optinit(URLContext *s)
{
	OPTVAL_INIT("timesec_beatheart",s->opt.rtsp.timesec_beatheart, 0, 30);
	s->opt.rtsp.tcp_func=&ffurl_tcp_protocol;
	s->opt.rtsp.rtp_func=&ffurl_rtp_protocol;
	s->opt.rtsp.udp_func=&ffurl_udp_protocol;
} 

static void * rtsp_heart(void * param)
{
	URLContext *s =(URLContext *)param;
	if(RTSP_LOWER_TRANSPORT_UDP!=s->opt.rtsp.lower_transport){
		return ms_null;
	}
	msthread_api_setName("rtsp_heart");
	RTSPContent *rtsp_ctt=s->priv_data;
	while(1){
	//maybe need to creat  thread
		if(ms_true==s->opt.rtsp.cmd_stopheart){
			ms_debug("--------stop rtsp_heart");
			s->opt.rtsp.cmd_stopheart=ms_false;
			return ms_null;
		}
		if(mstime_api_counterAsyncSec(&rtsp_ctt->timep,s->opt.rtsp.timesec_beatheart)){
			/* BEGIN: Added by sugao<sugao_cn@163.com>, 2019/11/11   PN:001,Server   ANNOUNCE used */		
			char aucReplyContext[38572]={0};	/* buffer to receive data */
			URLContext *purl_rtspctt=&rtsp_ctt->url_rtspctt;
			int len = msnet_api_epollRecv(purl_rtspctt->fd,aucReplyContext,sizeof(aucReplyContext),1);
			if(len>0){
				ms_verbose("[ReadReply-%d]:\r\n%s",len,aucReplyContext);
				rtsp_parse_announec(s,aucReplyContext) ;
			}
			/* END:   Added by sugao, 2019/11/11 */
			ms_verbose("has_getparamete,has_options:%d,%d",rtsp_ctt->public_info.has_getparameter,rtsp_ctt->public_info.has_options);
			if(ms_true==rtsp_ctt->public_info.has_getparameter){
				if ( RTSP_REPLY_OK!=rtsp_send_cmd(RTSPCMD_GET_PARAMETER, s,ms_true)	){
					ms_error("send GET_PARAMETER failed!");
				}
			}else{
				if ( RTSP_REPLY_OK!=rtsp_send_cmd(RTSPCMD_OPTIONS, s,ms_true)	){
					ms_error("send OPTIONS failed!");
				}
			}
		}
		ms_msleep(100);
	}
}
static int rtsp_open(URLContext *s)
 {
 	if(ms_true==s->is_connected){
		return 0;
	}
	ms_debug("Open url %s",s->url);
/*init var*/	
	rtsp_optinit( s);
 	RTSPContent *rtsp_ctt=ms_null;
	ms_mallocDes_goto(fail_mrtsp_ctt, rtsp_ctt, (sizeof(RTSPContent)), "RTSPContent",s->url);
	s->priv_data=rtsp_ctt;

	ms_byte *p = strchr(s->url, '?');
    	if (p) {
		ms_byte buf[256]={0};
        		if (msstr_api_findTag(buf, sizeof(buf), "client_host", p)) {
			ms_strcpy(rtsp_ctt->client_host, buf);
			ms_debug("find  client_host=%s",rtsp_ctt->client_host);
        		}
        		if (msstr_api_findTag(buf, sizeof(buf), "test_noin", p)) {
			rtsp_ctt->flag_test_noin=ms_true;
			ms_debug("find  flag_test_noin");
        		}
        		if (msstr_api_findTag(buf, sizeof(buf), "rtsp_transport", p)) {
			ms_strcpy(rtsp_ctt->rtsp_transport, buf);
			ms_debug("find  rtsp_transport=%s",rtsp_ctt->rtsp_transport);
        		}	
		if(s->flags&FLAG_WRITE){
			p = strchr(s->url, '?');	
			//drop  ?.....
			p[0]=0;
		}		
        	}
	URLContext *purl_rtspctt=s->opt.rtsp.url_rtspctt=&rtsp_ctt->url_rtspctt;
	
	URLProtocol *tcp_func=s->opt.rtsp.tcp_func;
	URLProtocol *rtp_func=s->opt.rtsp.rtp_func;
	s->is_rtspstream=ms_true;

	ms_string pmsenv_ptl_useragent=msenv_api_get(msenv_ptl_useragent,"MultiCoreServer");
	ms_verbose("%s=%s",msenv_ptl_useragent,pmsenv_ptl_useragent);
	ms_sprintfs(rtsp_ctt->useragent,"User-Agent: %s\r\n",pmsenv_ptl_useragent);


	#define MAX_REOPEN_TIMES	3
	ms_u08 times_reopen=0;
 reopen:	
 	//must be clear for reopen
 	ms_memset(rtsp_ctt->publics,0,sizeof(rtsp_ctt->publics));
	ms_memset(purl_rtspctt,0,sizeof(URLContext));
	//parse url pscUrl:to get url_info.protocol_name authorization url_info.hostname url_info.prot url_info.path
	msptc_api_urlSplit_urlinfo(&s->url_info,(ms_true==s->flag_location) ? s->location : s->url);	
	if(ms_buflen(s->url_info.authorization)>0){
		s->opt.rtsp.flag_authorization=ms_true;
		ms_errGoto(fail_mrtsp_ctt, "has authorization(%s)--url:%s!",s->url_info.authorization,purl_rtspctt->url);	
	}
	if (ms_unvalidport==s->url_info.port){
		s->url_info.port=ms_defaultport_rtsp;
	}
	
/*open rtsp connect over tcp*/
	purl_rtspctt->ifcIn=s->ifcIn;//pass throgh interfacein to tcp
	if(s->url_info.flag_ipv6){
		sprintf(purl_rtspctt->url,"tcp://[%s]:%d",s->url_info.hostname,s->url_info.port);
	}else{
		sprintf(purl_rtspctt->url,"tcp://%s:%d",s->url_info.hostname,s->url_info.port);
	}
	if(tcp_func->url_open(purl_rtspctt)<0){
		ms_errGoto(fail_cmd, "open %s failed!",purl_rtspctt->url);	
	}
	rtsp_ctt->server_type=RTSP_SERVER_RTP;	
	s->opt.rtsp.transport =RTSP_STATUS_TRANSPORT_RTP;
		
	//get rtsp_transport from env
	if(RTSP_LOWER_TRANSPORT_UNKNOW==s->opt.rtsp.lower_transport){
		ms_string prtsp_transport=rtsp_ctt->rtsp_transport;
		if(0==ms_buflen(rtsp_ctt->rtsp_transport)){
			prtsp_transport=msenv_api_get(msenv_ptl_rtsptransport,"udp");
			ms_verbose("no set transport ,get form env :%s=%s",msenv_ptl_rtsptransport,prtsp_transport);
		}else{
			ms_verbose("set transport:rtsp_transport:rtsp_transport=%s",prtsp_transport);
		}
		if(ms_strncmp_saeq(prtsp_transport, "tcp")){
			s->opt.rtsp.lower_transport=RTSP_LOWER_TRANSPORT_TCP;
		}else{
			s->opt.rtsp.lower_transport=RTSP_LOWER_TRANSPORT_UDP;
		}
	}
	if(s->flags&FLAG_WRITE){
/*send cmd to rtsp server*/
		ENUM_RTSP_STATUS rtsp_status=RTSP_UNKNOW;	
		rtsp_status=rtsp_send_cmd(RTSPCMD_OPTIONS, s,ms_false);
		if ( RTSP_REPLY_OK!=rtsp_status
			&&RTSP_REPLY_NOT_IMPLEMENTED!=rtsp_status){
			ms_errGoto(fail_cmd, "send OPTIONS failed!(%d)",rtsp_status);
		}		
		
		rtsp_status=rtsp_send_cmd(RTSPCMD_ANNOUNCE,s,ms_false);
		if ( RTSP_REPLY_OK!=rtsp_status){
			ms_errGoto(fail_cmd,"send ANNOUNCE failed!");
		}
		ms_debug("payload_type_video:%d",rtsp_ctt->payload_type_video);
		if(rtsp_ctt->payload_type_video==RTP_PAYLOADTYPE_DATA_MP2T){
			s->opt.rtsp.is_mpegts=ms_true;
		}
		rtsp_status=rtsp_send_cmd(RTSPCMD_SETUP,s,ms_false);
		if ( RTSP_REPLY_OK!=rtsp_status){
			if(RTSP_REPLY_TRANSPORT==rtsp_status
				&&ms_false==rtsp_ctt->has_tryother_transport){ 
				//close the rtcp sockect,and try tcp
				tcp_func->url_close(purl_rtspctt);
				s->opt.rtsp.lower_transport=RTSP_LOWER_TRANSPORT_TCP;
				s->is_connected=ms_false;
				rtsp_ctt->cseq_id= 0 ;
				rtsp_ctt->has_tryother_transport=ms_true;
				ms_infoGoto(reopen,"try tcp to open rtsp ...");
			}else{
				ms_errGoto(fail_cmd,"send SETUP failed!");
			}
		}
		//open rtp before send play cmd
		if(rtp_func->url_open(s)<0){
			ms_errGoto(fail, "open rtp_func failed!");	
		}
		rtsp_ctt->rtsp_state=RTSP_STATE_IDLE;
		rtsp_ctt->seek_timestamp=0;

		rtsp_status=rtsp_send_cmd(RTSPCMD_RECORD,s,ms_false);
		if ( RTSP_REPLY_OK!=rtsp_status){
			ms_errGoto(fail,"send RECORD failed!");
		}
	}else{
/*send cmd to rtsp server*/
		ENUM_RTSP_STATUS rtsp_status=RTSP_UNKNOW;	
		rtsp_status=rtsp_send_cmd(RTSPCMD_OPTIONS, s,ms_false);
		if ( RTSP_REPLY_OK!=rtsp_status
			&&RTSP_REPLY_NOT_IMPLEMENTED!=rtsp_status){
			ms_errGoto(fail_cmd, "send OPTIONS failed!(%d)",rtsp_status);
		}		
		rtsp_status=rtsp_send_cmd(RTSPCMD_DESCRIBE,s,ms_false);
		if ( RTSP_REPLY_OK!=rtsp_status){
			if(RTSP_REPLY_REOPEN==rtsp_status){    //If the statuscode is 302,and then change to the new url	
				//close the rtcp sockect
				tcp_func->url_close(purl_rtspctt);
				ms_strcpy(s->location, rtsp_ctt->location);	
				s->flag_location=ms_true;
				s->is_connected=ms_false;
				rtsp_ctt->cseq_id= 0 ;
				if((++times_reopen)>MAX_REOPEN_TIMES){
					ms_errGoto(fail_cmd,"reopen too many times!");
				}else{
					ms_debug("reopen %d times!",times_reopen);
				}
				ms_infoGoto(reopen,"Redirecting  url to (%s),and then open the new url ...",s->location);
			}else{
				ms_errGoto(fail_cmd,"send DESCRIBE failed!");
			}
		}
		ms_debug("payload_type_video:%d",rtsp_ctt->payload_type_video);
		if(rtsp_ctt->payload_type_video==RTP_PAYLOADTYPE_DATA_MP2T){
			s->opt.rtsp.is_mpegts=ms_true;
		}
		#if RTSP_DBG_CLOSE_SessionIncomplete
			ms_warGoto(fail_cmd,"This is a test of actively closing the connection when the session is not completed. Please do not open it in the official version.");
		#endif
		rtsp_status=rtsp_send_cmd(RTSPCMD_SETUP,s,ms_false);
		if ( RTSP_REPLY_OK!=rtsp_status){
			if(RTSP_REPLY_TRANSPORT==rtsp_status
				&&ms_false==rtsp_ctt->has_tryother_transport){ 
				//close the rtcp sockect,and try tcp
				tcp_func->url_close(purl_rtspctt);
				s->opt.rtsp.lower_transport=RTSP_LOWER_TRANSPORT_TCP;
				s->is_connected=ms_false;
				rtsp_ctt->cseq_id= 0 ;
				rtsp_ctt->has_tryother_transport=ms_true;
				ms_infoGoto(reopen,"try tcp to open rtsp ...");
			}else{
				ms_errGoto(fail_cmd,"send SETUP failed!");
			}
		}
		//open rtp before send play cmd
		if(RTSP_LOWER_TRANSPORT_TCP==s->opt.rtsp.lower_transport){
			if (mslock_api_init(&s->opt.rtsp.mslock_ctt,"rtsp_opt_lock",lockType_mutex) != 0) {
				ms_errGoto(fail_cmd, "[%s]mslock_api_initMutex failed!",purl_rtspctt->url);	
			}
			mslock_api_do(&s->opt.rtsp.mslock_ctt);
			s->opt.rtsp.thread_started=THREAD_STATE_UNKNOW;	
			mslock_api_undo(&s->opt.rtsp.mslock_ctt);
			if(ms_false==msstr_api_isCasestr(rtsp_ctt->rtsp_info, "rtsp(over tcp)")){
				ms_s08 tbuf[512]={0};
				ms_strcats(tbuf,rtsp_ctt->rtsp_info, "#%s", "rtsp(over tcp)");
			}
		}else if(RTSP_LOWER_TRANSPORT_UDP==s->opt.rtsp.lower_transport){
			if(ms_false==msstr_api_isCasestr(rtsp_ctt->rtsp_info, "rtsp(over udp")){
				ms_s08 tbuf[512]={0};
				ms_strcats(tbuf,rtsp_ctt->rtsp_info,"#rtsp(over udp,port:%d)",s->opt.rtsp.rtp_port);
			}
		}else{
			if(ms_false==msstr_api_isCasestr(rtsp_ctt->rtsp_info, "rtsp(over unknow)")){
				ms_s08 tbuf[512]={0};
				ms_strcats(tbuf,rtsp_ctt->rtsp_info, "#%s","rtsp(over unknow)");
			}
		}
		
		if(rtp_func->url_open(s)<0){
			ms_errGoto(fail, "open rtp_func failed!");	
		}
		if(RTSP_LOWER_TRANSPORT_TCP==s->opt.rtsp.lower_transport){
			mslock_api_do(&s->opt.rtsp.mslock_ctt);
			s->opt.rtsp.thread_started=THREAD_STATE_INIT;	
			mslock_api_undo(&s->opt.rtsp.mslock_ctt);
		}
		
		rtsp_ctt->rtsp_state=RTSP_STATE_IDLE;
		rtsp_ctt->seek_timestamp=0;

		rtsp_status=rtsp_send_cmd(RTSPCMD_PLAY,s,ms_false);
		if ( RTSP_REPLY_OK!=rtsp_status){
			ms_errGoto(fail,"send PLAY failed!");
		}
		if(RTSP_LOWER_TRANSPORT_TCP==s->opt.rtsp.lower_transport){
			//when rtp over tcp ,must be start thread after send play cmd
			if(ms_false==mslock_api_waitStateTimeout(&s->opt.rtsp.mslock_ctt, 
				&s->opt.rtsp.thread_started, 
				THREAD_STATE_STARTING, 
				THREAD_STATE_START, ms_msseconds(1),ms_mseconds(100))){		//wait 1s
				rtsp_send_cmd(RTSPCMD_TEARDOWN,s,ms_false);
				ms_errGoto(fail_start_thread,"Cannot start thread(rtp over tcp)!");
			}
		}
		if(RTSP_LOWER_TRANSPORT_UDP==s->opt.rtsp.lower_transport){
			if(msthread_api_create(&s->opt.rtsp.msthread_ctt_heart, "rtsp_heart",rtsp_heart, s)<0){
				ms_errGoto(fail,"msthread_api_create (rtsp_heart) faild"); 
			}
		}
	}
	s->is_connected=ms_true;
	return 0;
fail:
	if(RTSP_LOWER_TRANSPORT_TCP==s->opt.rtsp.lower_transport){
		if(ms_false==mslock_api_waitStateTimeout(&s->opt.rtsp.mslock_ctt, 
			&s->opt.rtsp.thread_started, 
			THREAD_STATE_STOPPING, 
			THREAD_STATE_STOP, ms_msseconds(3),ms_mseconds(100))){
			ms_error("Cannot stop thread(rtp over tcp)!");
		}
	}
fail_start_thread:		
	if(ms_null!=rtp_func){
		rtp_func->url_close(s);
	}
	if(RTSP_LOWER_TRANSPORT_TCP==s->opt.rtsp.lower_transport){
		mslock_api_deinit(&s->opt.rtsp.mslock_ctt);
	}
fail_cmd:	
	if(ms_null!=tcp_func&&ms_null!=purl_rtspctt){
		tcp_func->url_close(purl_rtspctt);
	}
	s->opt.rtsp.is_mpegts=ms_true;
fail_murl_rtspctt:

fail_mrtsp_ctt:
	ms_deMalloc(rtsp_ctt);
	s->is_connected=ms_false;
	return -1;
 }

static int rtsp_read(URLContext *s ,  unsigned char *buf, int size)
{
	if(ms_false==s->is_connected){
		return -1;
	}
	URLProtocol *rtp_func=s->opt.rtsp.rtp_func;
	return rtp_func->url_read(s ,  buf, size);
}

static int rtsp_close(URLContext *s);

static int rtsp_write(URLContext *s,  unsigned char *buf, int size)
{
	if(ms_false==s->is_connected){
		return -1;
	}
	URLProtocol *rtp_func=s->opt.rtsp.rtp_func;
	RTSPContent *rtsp_ctt=s->priv_data;
	if((RTSP_LOWER_TRANSPORT_UDP==s->opt.rtsp.lower_transport)
		||(ms_true==rtsp_ctt->flag_test_noin)
#if RTSP_DBG_OVERTCP_SENDHEART
		||(RTSP_LOWER_TRANSPORT_TCP==s->opt.rtsp.lower_transport)
#endif
	){
		if(mstime_api_counterAsyncSec(&rtsp_ctt->timep,5)){
			ms_verbose("has_getparamete,has_options:%d,%d",rtsp_ctt->public_info.has_getparameter,rtsp_ctt->public_info.has_options);
			if(ms_true==rtsp_ctt->public_info.has_getparameter){
				if ( RTSP_REPLY_OK!=rtsp_send_cmd(RTSPCMD_GET_PARAMETER, s,ms_true)	){
					rtsp_close(s);
					ms_errRet(-1,"send GET_PARAMETER failed!");
				}
			}else{
				if ( RTSP_REPLY_OK!=rtsp_send_cmd(RTSPCMD_OPTIONS, s,ms_true)	){
					rtsp_close(s);
					ms_errRet(-1,"send OPTIONS failed!");
				}
			}
		}
	}	
	if(ms_true==rtsp_ctt->flag_test_noin){	
		static  ms_s32 test_noin=0;
		if(++test_noin>1000){
			return size;
		}else{
			return rtp_func->url_write(s,buf, size);
		}
	}else{
		return rtp_func->url_write(s,buf, size);
	}
}

static int rtsp_close(URLContext *s)
{
	if(ms_false==s->is_connected){
		return 0;
	}
	RTSPContent *rtsp_ctt=s->priv_data;
	URLContext *purl_rtspctt=&rtsp_ctt->url_rtspctt;
	URLProtocol *rtp_func=s->opt.rtsp.rtp_func;
	URLProtocol *tcp_func=s->opt.rtsp.tcp_func;
	if(!(s->flags&FLAG_WRITE)){
		if(RTSP_LOWER_TRANSPORT_TCP==s->opt.rtsp.lower_transport){
			//when rtp over tcp ,must be close thread before send teardown cmd
			if(ms_false==mslock_api_waitStateTimeout(&s->opt.rtsp.mslock_ctt, 
				&s->opt.rtsp.thread_started, 
				THREAD_STATE_STOPPING, 
				THREAD_STATE_STOP, ms_msseconds(3),ms_mseconds(100))){	//wait 3s
				ms_error("Cannot stop thread(rtp over tcp)!");
			}
		}
	}
	
	if(!(s->flags&FLAG_WRITE)){
		if(RTSP_LOWER_TRANSPORT_UDP==s->opt.rtsp.lower_transport){
			ms_debug("====>Stop rtsp_heart task");
			if(ms_true==s->opt.rtsp.msthread_ctt_heart.init){
				s->opt.rtsp.cmd_stopheart=ms_true;
				ms_u64 sys_base=mstime_api_sec();
				while(ms_true==s->opt.rtsp.cmd_stopheart){
					ms_debug("Wait for rtsp_heart stop");
					ms_msleep(30);
					if(ms_true==mstime_api_counterAsyncSec(&sys_base, ms_seconds(3))){
						break;
					}
				}
				msthread_api_destroy(&s->opt.rtsp.msthread_ctt_heart);
			}else{
				ms_waring("no need to destroy rtsp_heart task(msthread_ctt_heart.init=%d)",s->opt.rtsp.msthread_ctt_heart.init);
			}
		}
	}
	
	rtsp_send_cmd(RTSPCMD_TEARDOWN,s,ms_false);
	if(ms_null!=rtp_func){
		rtp_func->url_close(s);
	}
	if(!(s->flags&FLAG_WRITE)){
		if(RTSP_LOWER_TRANSPORT_TCP==s->opt.rtsp.lower_transport){
			mslock_api_deinit(&s->opt.rtsp.mslock_ctt);
		}
	}
	if(ms_null!=tcp_func&&ms_null!=purl_rtspctt){
		tcp_func->url_close(purl_rtspctt);
	}	
	if(ms_null!=rtsp_ctt){
		ms_free(rtsp_ctt);
		rtsp_ctt=ms_null;
	}
	s->is_connected=ms_false;
	return 0;
}
static ms_string rtsp_info(URLContext *s)
{
	if(ms_false==s->is_connected){
		return ms_null;
	}
	RTSPContent *rtsp_ctt=s->priv_data;
	return rtsp_ctt->rtsp_info;
}
URLProtocol ffurl_rtsp_protocol={
	.nameDes	= msptc_getPtcNameDes(msptc_rtsp),
    .url_open	= rtsp_open,
    .url_read	= rtsp_read,
    .url_write	= rtsp_write,
    .url_close	= rtsp_close,
    .url_info	=rtsp_info,
    .flags		=STREAM_LIVE_VOD,
     .priv_func={
		.rtsp.play	=rtsp_play,	
		.rtsp.pause	=rtsp_pause,
		.rtsp.seek	=rtsp_seek,		
	},   	
    .next	=ms_null,
};
#undef RTSP_C
